import { ApiController, ApiPost, ApiGet, ApiBodyParam, DataType, Request } from 'xl-ts-plugins/router'
import { getRepository, getConnection, QueryRunner } from 'xl-ts-plugins/typeorm'
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'
import TokenUserMidware from '../midware/token-user-midware'
import { User } from '../entity/User'
import { UserAmount } from '../entity/UserAmount'
import { UserAmountRecord } from '../entity/UserAmountRecord'
import { mergeValidator } from '../utils/validator'
import { ajaxResultFail, ajaxResultSuccess, ajaxLogFail } from '../utils/custom-result'

/**
 * 用户金额控制器
*/
@ApiController(
  { path: '/userAmount', tags: [ 'UserAmount' ] },
  TokenUserMidware.midware
)
export default class UserAmountController {

  @ApiGet({ path: '/getUserAmount', summary: '获取用户余额' })
  async getUserAmount(@Request req) {
    const tokenUser = TokenUserMidware.getTokenData(req)

    const result = await getRepository(UserAmount).findOne({
      userId: tokenUser.userId
    })

    return ajaxResultSuccess(result.amount)
  }


  @ApiPost({ path: '/transferAccounts', summary: '向某个用户转账' })
  async transferAccounts(
    @Request req,
    @ApiBodyParam({ name: 'targetUserId', description: '目标用户id', type: DataType.Number }) targetUserId: number,
    @ApiBodyParam({ name: 'money', description: '转账金额', type: DataType.Number }) money: number
  ) {
    const tokenUser = TokenUserMidware.getTokenData(req)

    // 检查字段
    const errMsg = mergeValidator([{
      handler: !(targetUserId > 0),
      errorMsg: '目标用户id不能为空'
    }, {
      handler: !(money > 0),
      errorMsg: '转账金额不正确'
    }])

    if (errMsg) {
      return ajaxResultFail(errMsg)
    }

    // 目标用户
    const targetUser = await getRepository(User).findOne(targetUserId)
    if (!targetUser) {
      return ajaxResultFail('目标用户不存在')
    }

    // 我的余额数据
    const myUserAmount = await findOrInsertUserAmount(tokenUser.userId)

    // 检查我的余额是否足够
    if (myUserAmount.amount < money) {
      return ajaxResultFail('你的余额不足')
    }

    // 目标用户的余额数据
    const targetUserAmount = await findOrInsertUserAmount(targetUser.userId)


    // 获取连接并创建新的 queryRunner
    const queryRunner = getConnection().createQueryRunner()

    try {
      // 使用新 queryRunner 建立数据库连接
      await queryRunner.connect()
      // 开始事务
      await queryRunner.startTransaction()

      // 减少当前用户余额
      await updateAmountByUserId(tokenUser.userId, {
        amount: () => `amount - ${money}`
      }, queryRunner).execute()

      // 添加余额减少记录
      await insertUserAmountRecord({
        userAmountId: myUserAmount.userAmountId,
          changeAmount: -money,
          description: `向 ${targetUser.userName} 转账 ${money} 元`
      }).execute()

      // 增加目标用户余额
      await updateAmountByUserId(targetUser.userId, {
        amount: () => `amount + ${money}`
      }, queryRunner).execute()

      // 添加余额增加记录
      await insertUserAmountRecord({
        userAmountId: targetUserAmount.userAmountId,
        changeAmount: money,
        description: `${tokenUser.userName} 向我转账 ${money} 元`
      }).execute()

      // 提交事务
      await queryRunner.commitTransaction()

      return ajaxResultSuccess('转账成功')
    } catch (error) {
      // 有错误做出回滚更改
      await queryRunner.rollbackTransaction()
      ajaxLogFail(error)
    }
  }
}

// 检查是否有某个用户的余额数据，没有就创建，并返回
export const findOrInsertUserAmount = async (userId: number) => {
  const userAmount = await getRepository(UserAmount).findOne({ userId })

  if (!userAmount) {
    const insertResult = await getRepository(UserAmount).insert({ userId })

    return await getRepository(UserAmount).findOne({ userAmountId: insertResult.raw.insertId })
  }

  return userAmount
}

// 修改某个用户的余额
export const updateAmountByUserId = (userId: number, query: QueryDeepPartialEntity<UserAmount>, queryRunner?: QueryRunner) => {
  return getRepository(UserAmount).createQueryBuilder(null, queryRunner)
    .update()
    .set(query)
    .where(`user_id = ${userId}`)
}

// 增加余额变动记录
export const insertUserAmountRecord = (query: QueryDeepPartialEntity<UserAmountRecord>, queryRunner?: QueryRunner) => {
  return getRepository(UserAmountRecord).createQueryBuilder(null, queryRunner)
    .insert()
    .into(UserAmountRecord)
    .values(query)
}